package pkg

import (
	"bytes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"github.com/tjfoc/gmsm/sm4"
	"math/rand"
	"strings"
	"time"
)

const SM4_HEADER = "SM4_"

var SM4Iv = "04TzMuvkHm_EZnHm"
var sm4Letter = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

type SM4Encryptor struct {
}

type sm4EncodeValue struct {
	cipherText []byte
}

// NewSM4EncodeValue创建新的SM4加密后得值
func NewSM4EncodeValue(cipherText []byte) *sm4EncodeValue {
	return &sm4EncodeValue{
		cipherText: cipherText,
	}
}

// EncodeValue 返回一个有特征得SM4得加密值
func (e *sm4EncodeValue) EncodeSm4Value() string {
	encodeText := base64.StdEncoding.EncodeToString(e.cipherText)
	return fmt.Sprintf("%s%s", SM4_HEADER, encodeText)
}

type sm4DecodeValue struct {
	cipherText []byte
}

// NewSM4DecodeValue创建新的SM4解密后得值
func NewSM4DecodeValue(cipherText []byte) *sm4DecodeValue {
	return &sm4DecodeValue{cipherText: cipherText}
}

// DecodeSm4Value 返回一个去特征得SM4解密值
func (d *sm4DecodeValue) DecodeSm4Value() ([]byte, error) {
	if !strings.HasPrefix(string(d.cipherText), SM4_HEADER) {
		return d.cipherText, nil
	}
	return base64.StdEncoding.DecodeString(
		strings.TrimPrefix(string(d.cipherText), SM4_HEADER))
}

// IsSM4Value 判断是否为sm4加密后的值
func (d *sm4DecodeValue) IsSM4Value() bool {
	return strings.HasPrefix(string(d.cipherText), SM4_HEADER)
}
func paddingLastGroup(plainText []byte, blockSize int) []byte {
	//1.计算最后一个分组中明文后需要填充的字节数
	padNum := blockSize - len(plainText)%blockSize
	//2.将字节数转换为byte类型
	char := []byte{byte(padNum)}
	//3.创建切片并初始化
	newPlain := bytes.Repeat(char, padNum)
	//4.将填充数据追加到原始数据后
	return append(plainText, newPlain...)
}

// 去掉明文后面的填充数据
func unPaddingLastGroup(plantText []byte) []byte {
	//1.拿到切片中的最后一个字节
	length := len(plantText)
	lastChar := int(plantText[length-1])
	//2.将最后一个数据转换为整数
	number := int(lastChar)
	return plantText[:(length - number)]
}

// SM4Encrypt 用于sm4Key 加密
func (e SM4Encryptor) Encrypt(sm4Key, data string) (string, error) {
	//字符串转byte切片
	plainText := []byte(data)
	//建议从配置文件中读取秘钥，进行统一管理
	//SM4Key := "Uv6tkf2M3xYSRuFv"
	//todo 注意：iv需要是随机的，进一步保证加密的安全性，将iv的值和加密后的数据一起返回给外部
	iv := []byte(SM4Iv)
	key := []byte(sm4Key)
	//实例化sm4加密对象
	block, err := sm4.NewCipher(key)
	if err != nil {
		return "", err
	}
	//明文数据填充
	paddingData := paddingLastGroup(plainText, block.BlockSize())
	//声明SM4的加密工作模式
	blockModel := cipher.NewCBCEncrypter(block, iv)
	//为填充后的数据进行加密处理
	cipherText := make([]byte, len(paddingData))
	//使用CryptBlocks这个核心方法，将paddingData进行加密处理，将加密处理后的值赋值到cipherText中
	blockModel.CryptBlocks(cipherText, paddingData)
	encode := NewSM4EncodeValue(cipherText)
	return encode.EncodeSm4Value(), nil
}

// SM4解密 传入string 输出string
func (e SM4Encryptor) Decrypt(sm4Key string, data string) (string, error) {
	decode := NewSM4DecodeValue([]byte(data))
	if !decode.IsSM4Value() {
		return data, nil
	}
	contentBytes, err := decode.DecodeSm4Value()
	//秘钥
	//SM4Key := "Uv6tkf2M3xYSRuFv"
	//iv是Initialization Vector，初始向量，
	//SM4Iv := "04TzMuvkHm_EZnHm"
	iv := []byte(SM4Iv)
	key := []byte(sm4Key)
	block, err := sm4.NewCipher(key)
	if err != nil {
		panic(err)
	}
	//CBC模式 优点：具有较好的安全性，能够隐藏明文的模式和重复性。 缺点：加密过程是串行的，不适合并行处理。

	blockModel := cipher.NewCBCDecrypter(block, iv)
	blockModel.CryptBlocks(contentBytes, contentBytes)
	//去掉明文后面的填充数据
	plantText := unPaddingLastGroup(contentBytes)
	return string(plantText), nil
}

// RandKey 随机生成32位  aes key
func (e SM4Encryptor) RandKey() string {
	b := make([]rune, 16)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = sm4Letter[r.Intn(62)]
	}
	return string(b)
}
